package com.fangyi.yunmusic.aspect;

import com.fangyi.yunmusic.config.ElasticSearchConfig;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class ElasticSearchAspect {

    private Logger logger = LoggerFactory.getLogger(ElasticSearchAspect.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ElasticSearchConfig elasticSearchConfig;

    // 异步执行类
    @Autowired
    private ElasticSearchAsync elasticSearchAsync;

    @Pointcut("execution(* com.fangyi.yunmusic.mapper..*.*(..))")
    private void check(){}

    @Around("check()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        Signature method = pjp.getSignature();

        // 得到方法名
        String methodName = method.getName();
        // 从方法得到切到的类或接口的全包类名
        String className = method.getDeclaringTypeName();

        // 得到方法参数
        Object[] args = pjp.getArgs();
        if (args.length != 1) {
            return pjp.proceed();
        }
        Object arg = args[0];

        // 判断是否需要切，不需要则直接执行原代码
        if (!isCut(className, methodName)) {
            return pjp.proceed();
        }

        // 执行原方法（先更新数据库，再更新ES)
        Object object = pjp.proceed();

        // ---------开始切面----------
        // 得到repository
        String repositoryClassName = elasticSearchConfig.getRepositoryMap().get(className);
        Class<?> repositoryClass = Class.forName(repositoryClassName);
        ElasticsearchRepository repository = (ElasticsearchRepository) applicationContext.getBean(repositoryClass);
        // 得到document
        String documentClassName = elasticSearchConfig.getDocumentMap().get(className);
        Class<?> documentClass = Class.forName(documentClassName);
        Object document = documentClass.newInstance();

        if (methodName.equals("insert") || methodName.equals("insertSelective")) {
            // insert
            // 为document赋值
            BeanUtils.copyProperties(arg, document);
            // 插入ES
//            repository.save(document);
            // 异步执行ES操作
            elasticSearchAsync.save(repository, document);
        } else if (methodName.equals("updateByPrimaryKey")) {
            // update
            // 为document赋值
            BeanUtils.copyProperties(arg, document);
            // 更新到ES
//            repository.save(document);
            elasticSearchAsync.save(repository, document);
        } else if (methodName.equals("deleteByPrimaryKey")) {
            // delete
            // 此时arg实际为ID
            // 在ES删除
//            repository.deleteById(arg);
            elasticSearchAsync.deleteById(repository, arg);
        }
        // --------切面结束--------

        return object;
    }

    private boolean isCut(String className, String methodName) {
        if (!elasticSearchConfig.getRepositoryMap().containsKey(className) || !elasticSearchConfig.getDocumentMap().containsKey(className)) {
            return false;
        }

        if (methodName.equals("insert") || methodName.equals("insertSelective")) {
            // insert
        } else if (methodName.equals("updateByPrimaryKey")) {
            // update
        } else if (methodName.equals("deleteByPrimaryKey")) {
            // delete
        } else {
            return false;
        }

        return true;
    }

    // ElasticSearch异步执行类
    @Component
    public static class ElasticSearchAsync {

        private Logger logger = LoggerFactory.getLogger(ElasticSearchAsync.class);

        @Async
        public void save(ElasticsearchRepository repository, Object value) {
            logger.info("ES开始保存");
            try {
                repository.save(value);
            } catch (Exception e) {
                // 失败就暂停一下，再重试
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e2) {

                }
                repository.save(value);
            }
            logger.info("ES保存成功");
        }

        @Async
        public void deleteById(ElasticsearchRepository repository, Object value) {
            logger.info("ES开始删除");
            try {
                repository.deleteById(value);
            } catch (Exception e) {
                // 失败就暂停一下，再重试
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e2) {

                }
                deleteById(repository, value);
            }

            logger.info("ES删除成功");
        }

    }

}
